package cn.epsilon3.service.impl;

import cn.epsilon3.domain.e3.dto.update.E3ResourceUpdate;
import cn.hutool.core.collection.CollectionUtil;
import cn.epsilon3.common.E3BaseService;
import cn.epsilon3.constant.Base;
import cn.epsilon3.constant.ResourceTypeEnum;
import cn.epsilon3.domain.e3.dto.add.E3ResourceAdd;
import cn.epsilon3.domain.e3.dto.query.E3ResourceQuery;
import cn.epsilon3.domain.e3.po.E3ResourcePO;
import cn.epsilon3.domain.e3.vo.E3ResourceVO;
import cn.epsilon3.exception.Epsilon3ReturnException;
import cn.epsilon3.utils.AssertUtils;
import cn.epsilon3.utils.CollectionsUtils;
import cn.epsilon3.mybatis.mapper.E3ResourceMapper;
import cn.epsilon3.service.E3ResourceService;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 菜单资源(E3ResourcePO)表服务实现类
 *
 * @author makejava
 * @since 2023-11-12 21:49:57
 */
@Service("e3ResourceService")
public class E3ResourceServiceImpl extends E3BaseService implements E3ResourceService {

    @Resource
    private E3ResourceMapper e3ResourceMapper;

    /**
     * 查询当前登录角色对应的资源树
     *
     * @return 可达资源反参
     */
    @Override
    public List<E3ResourceVO> queryRoleResource(String roleId) {
        AssertUtils.isBlank(roleId, "当前用户暂未分配角色");
        return E3BaseService.getLoadResourceTreeCache(roleId);
    }

    /**
     * 条件查询资源
     *
     * @param resourceQuery 查询条件
     * @return 资源树
     */
    @Override
    public List<E3ResourceVO> list(E3ResourceQuery resourceQuery) {
        List<E3ResourceVO> list = e3ResourceMapper.list(resourceQuery);
        List<E3ResourceVO> resourceTree = CollectionsUtils.generateResourceTree(list, Base.STR0);
        return CollectionUtil.isEmpty(resourceTree) ? list : resourceTree;
    }

    /**
     * 新增资源
     *
     * @param resourceAdd 新增资源数据
     */
    @Override
    @Transactional(rollbackFor = Epsilon3ReturnException.class)
    public void add(E3ResourceAdd resourceAdd) {
        String resourceName = resourceAdd.getResourceName();
        String resourceType = resourceAdd.getResourceType();
        String resourceIcon = resourceAdd.getResourceIcon();
        Integer resourceLevel = resourceAdd.getResourceLevel();
        String parentId = resourceAdd.getParentId();

        E3ResourcePO resource = new E3ResourcePO();
        resource.setResourceName(resourceName);
        resource.setResourceType(resourceType);
        resource.setIconId(resourceIcon);
        resource.setLevel(resourceLevel);
        resource.setParentId(parentId);

        String parentType = AssertUtils.isFalseRt(parentId.equals(Base.STR0), parentId, true, "上级资源类型错误", () -> {
            E3ResourcePO parentResource = e3ResourceMapper.queryById(parentId);
            AssertUtils.isNull(parentResource, "上级资源不存在");
            return parentResource.getResourceType();
        });

        resourceNameIsExist(resourceName, resourceType, e3ResourceMapper, false, null);

        ResourceTypeEnum resourceTypeEnum = getResourceTypeEnum(resourceType);

        switch (resourceTypeEnum) {
            case CATALOG:
                checkParentIsCatalog(parentId, parentType);
                break;
            case ROUTER:
                checkParentIsCatalog(parentId, parentType);
                String resourcePath = resourceAdd.getResourcePath();
                AssertUtils.isBlank(resourcePath, "路由路径不能为空");
                E3ResourcePO queryResourcePath = e3ResourceMapper.selectOne(E3BaseService.queryWrappers(E3ResourcePO.class).eq(E3ResourcePO::getPath, resourcePath));
                AssertUtils.isNotNull(queryResourcePath, "资源路径已存在");
                String resourceParams = resourceAdd.getResourceParams();
                resource.setPath(resourcePath);
                resource.setParams(resourceParams);
                break;
            case FUNCTION:
                AssertUtils.isTrue(parentType.equals(ResourceTypeEnum.ROUTER.getType()), "上级资源类型必须为路由");
                String resourcePerms = resourceAdd.getResourcePerms();
                AssertUtils.isBlank(resourcePerms, "资源权限不能为空");
                E3ResourcePO queryResourcePermission = e3ResourceMapper.selectOne(E3BaseService.queryWrappers(E3ResourcePO.class).eq(E3ResourcePO::getPermission, resourcePerms));
                AssertUtils.isNotNull(queryResourcePermission, "资源权限已存在");
                resource.setPermission(resourcePerms);
                break;
            default:
                throw new Epsilon3ReturnException("资源类型错误");
        }
        AssertUtils.isTrue(Base.JDBC_TRUE == e3ResourceMapper.insert(resource), "新增资源失败");
    }

    /**
     * 获取resourceType枚举
     *
     * @param resourceType 资源类型
     * @return 资源类型枚举
     */
    private static ResourceTypeEnum getResourceTypeEnum(String resourceType) {
        return ResourceTypeEnum.valuesOf(resourceType);
    }


    /**
     * 修改资源
     *
     * @param resourceUpdate 修改资源数据
     */
    @Override
    @Transactional(rollbackFor = Epsilon3ReturnException.class)
    public void update(E3ResourceUpdate resourceUpdate) {
        String resourceId = resourceUpdate.getResourceId();
        String resourceName = resourceUpdate.getResourceName();
        E3ResourcePO resource = e3ResourceMapper.queryById(resourceId);
        String resourceType = resource.getResourceType();

        resourceNameIsExist(resourceName, resourceType, e3ResourceMapper, true, resourceId);

        ResourceTypeEnum resourceTypeEnum = getResourceTypeEnum(resourceType);

        E3ResourcePO updateResource = new E3ResourcePO();
        updateResource.setId(resourceUpdate.getResourceId());
        updateResource.setResourceName(resourceUpdate.getResourceName());
        updateResource.setLevel(resourceUpdate.getResourceLevel());
        updateResource.setIconId(updateResource.getIconId());
        switch (resourceTypeEnum) {
            case ROUTER:
                String resourcePath = resourceUpdate.getResourcePath();
                String resourceParams = resourceUpdate.getResourceParams();
                AssertUtils.isBlank(resourcePath, "资源路径不能为空");
                E3ResourcePO pathResource = e3ResourceMapper.selectOne(queryWrappers(E3ResourcePO.class).eq(E3ResourcePO::getPath, resourcePath));
                verifyResourceIsExistByResourceId(resourceId, pathResource);
                updateResource.setPath(resourcePath);
                updateResource.setParams(resourceParams);
                break;
            case FUNCTION:
                String resourcePerms = resourceUpdate.getResourcePerms();
                AssertUtils.isBlank(resourcePerms, "资源权限不能为空");
                E3ResourcePO permsResource = e3ResourceMapper.selectOne(queryWrappers(E3ResourcePO.class).eq(E3ResourcePO::getPermission, resourcePerms));
                verifyResourceIsExistByResourceId(resourceId, permsResource);
                updateResource.setPermission(resourcePerms);
                break;
            default:
                throw new Epsilon3ReturnException("资源类型错误");
        }
        AssertUtils.isTrue(Base.JDBC_TRUE == e3ResourceMapper.updateById(updateResource), "更新失败，请重试");
    }

    /**
     * 通过resourceId校验资源是否存在
     *
     * @param resourceId 资源id
     * @param pathResource 资源实体
     */
    private static void verifyResourceIsExistByResourceId(String resourceId, E3ResourcePO pathResource) {
        if (ObjectUtil.isNotNull(pathResource)) {
            AssertUtils.isTrue(resourceId.equals(pathResource.getId()), "资源路径已存在");
        }
    }

    /**
     * 删除资源
     *
     * @param resourceId 资源id
     */
    @Override
    @Transactional(rollbackFor = Epsilon3ReturnException.class)
    public void delete(String resourceId) {
        AssertUtils.isBlank(resourceId, "资源ID不能为空");
        AssertUtils.isTrue(Base.JDBC_TRUE == e3ResourceMapper.delete(updateWrappers(E3ResourcePO.class)
                        .eq(E3ResourcePO::getId, resourceId)),
                "删除失败，请重试");

        List<String> ids = new ArrayList<>();
        recursiveFindChildIds(resourceId, ids, e3ResourceMapper);

        AssertUtils.isTrue(CollectionUtil.isNotEmpty(ids), () -> {
            e3ResourceMapper.deleteBatchIds(ids);
        });

    }

    /**
     * 判断资源名称是否存在
     *
     * @param resourceName     资源名称
     * @param resourceType     资源类型
     * @param e3ResourceMapper 资源mapper
     * @param self             是否检查自身 true 检查自身 false 不检查自身
     * @param resourceId       资源id
     */
    private static void resourceNameIsExist(String resourceName,
                                            String resourceType,
                                            E3ResourceMapper e3ResourceMapper,
                                            boolean self,
                                            String resourceId) {
        LambdaQueryWrapper<E3ResourcePO> queryWrapper = E3BaseService.queryWrappers(E3ResourcePO.class)
                .eq(E3ResourcePO::getResourceName, resourceName)
                .eq(E3ResourcePO::getResourceType, resourceType);
        queryWrapper = self ? queryWrapper.ne(E3ResourcePO::getId, resourceId) : queryWrapper;
        E3ResourcePO queryResourceName = e3ResourceMapper.selectOne(queryWrapper);
        AssertUtils.isNotNull(queryResourceName, "当前资源类型下该资源名称已存在");
    }

    /**
     * 检验上级是否为目录
     *
     * @param parentId   上级资源id
     * @param parentType 父类型
     */
    private static void checkParentIsCatalog(String parentId,
                                             String parentType) {
        if (!parentId.equals(Base.STR0)) {
            AssertUtils.isTrue(parentType.equals(ResourceTypeEnum.CATALOG.getType()), "上级资源类型必须为目录");
        }
    }

    /**
     * 递归查询子资源节点
     *
     * @param resourceId 最高级资源id
     * @param ids 子资源节点id集合
     * @param e3ResourceMapper mapper
     */
    private static void recursiveFindChildIds(String resourceId,
                                              List<String> ids,
                                              E3ResourceMapper e3ResourceMapper) {
        List<String> resourceIds = e3ResourceMapper.selectObjs(queryWrappers(E3ResourcePO.class)
                        .eq(E3ResourcePO::getParentId, resourceId))
                .stream().map(String::valueOf)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(resourceIds)) {
            resourceIds.forEach((childId) -> {
                ids.add(childId);
                recursiveFindChildIds(childId, ids, e3ResourceMapper);
            });
        }
    }
}
